package falstad;

import java.awt.Event;
import java.util.Arrays;
import java.util.Collections;

public class WizardRobot extends StandardRobot implements Robot {

	Maze maze;
	
	public WizardRobot(Maze m)
	{
		super(m);
		this.maze = m;
	}
	
	private int[] getPositionOfLeftCell()
	{
		int[] position;
		if (getCurrentDirection() == Constants.TOP)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.LEFT[0] ,getCurrentPosition()[1] + Constants.LEFT[1]};
		}
		else if (getCurrentDirection() == Constants.LEFT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.BOTTOM[0] ,getCurrentPosition()[1] + Constants.BOTTOM[1]};
		}
		else if (getCurrentDirection() == Constants.BOTTOM)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.RIGHT[0] ,getCurrentPosition()[1] + Constants.RIGHT[1]};
		}
		else if (getCurrentDirection() == Constants.RIGHT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.TOP[0] ,getCurrentPosition()[1] + Constants.TOP[1]};
		}
		else
		{
			position = new int[]{0,0};
		}
		return position;
	}
	
	private int[] getPositionOfRightCell()
	{
		int[] position;
		if (getCurrentDirection() == Constants.RIGHT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.BOTTOM[0] ,getCurrentPosition()[1] + Constants.BOTTOM[1]};
		}
		else if (getCurrentDirection() == Constants.TOP)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.RIGHT[0] ,getCurrentPosition()[1] + Constants.RIGHT[1]};
		}
		else if (getCurrentDirection() == Constants.LEFT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.TOP[0] ,getCurrentPosition()[1] + Constants.TOP[1]};
		}
		else if (getCurrentDirection() == Constants.BOTTOM)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.LEFT[0] ,getCurrentPosition()[1] + Constants.LEFT[1]};
		}
		else
		{
			position = new int[]{0,0};
			System.out.println("Invalid Direction");
		}
		return position;
	}
	
	private int[] getPositionOfBehindCell()
	{
		int[] position;
		if (getCurrentDirection() == Constants.RIGHT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.LEFT[0] ,getCurrentPosition()[1] + Constants.LEFT[1]};
		}
		else if (getCurrentDirection() == Constants.TOP)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.BOTTOM[0] ,getCurrentPosition()[1] + Constants.BOTTOM[1]};
		}
		else if (getCurrentDirection() == Constants.LEFT)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.RIGHT[0] ,getCurrentPosition()[1] + Constants.RIGHT[1]};
		}
		else if (getCurrentDirection() == Constants.BOTTOM)
		{
			position = new int[] {getCurrentPosition()[0] + Constants.TOP[0] ,getCurrentPosition()[1] + Constants.TOP[1]};
		}
		else
		{
			position = new int[]{0,0};
		}
		return position;
	}
	
	//Returns the degree indicating the turn that the robot should make to be closer to the exit.
	private int findDirectionClosestToExit()
	{
		int ahead = Integer.MAX_VALUE, behind = Integer.MAX_VALUE, left = Integer.MAX_VALUE, right = Integer.MAX_VALUE;
		int degree = 0;
		try {
			if (distanceToObstacleAhead()!= 0 || canSeeGoalAhead())
			{
				ahead = maze.mazedists.getDistance(getCurrentPosition()[0] + getCurrentDirection()[0], getCurrentPosition()[1] + getCurrentDirection()[1]);
			}
			if (distanceToObstacleOnLeft() != 0|| canSeeGoalOnLeft())
			{
				left = maze.mazedists.getDistance(getPositionOfLeftCell()[0] , getPositionOfLeftCell()[1]);
			}
			if (distanceToObstacleOnRight() != 0 || canSeeGoalOnRight())
			{
				right = maze.mazedists.getDistance(getPositionOfRightCell()[0] , getPositionOfRightCell()[1]);
			}
			if (distanceToObstacleBehind() != 0 || canSeeGoalBehind())
			{
				behind = maze.mazedists.getDistance(getPositionOfBehindCell()[0] , getPositionOfBehindCell()[1]);
			}
		}
		//ArrayIndexOutOfBoundsException will be called if the getDistance method tries to find the distance of a cell outside the maze
		catch (ArrayIndexOutOfBoundsException e)
		{
			try{
				if (canSeeGoalAhead())
				{
					ahead = Integer.MIN_VALUE;
				}
				else if(canSeeGoalOnLeft())
				{
					left = Integer.MIN_VALUE;
				}
				else if (canSeeGoalOnRight())
				{
					right = Integer.MIN_VALUE;
				}
				else if (canSeeGoalBehind())
				{
					behind = Integer.MIN_VALUE;
				}
			}
			catch (UnsupportedMethodException e2) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			catch(ArrayIndexOutOfBoundsException e2)
			{
				//Do Nothing
			}
			
		}
		catch (UnsupportedMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
			
		int smallest = Collections.min(Arrays.asList(ahead, left, right, behind));
			 

		if (smallest == ahead)
			 {
				 degree = 0;
			 }
			else if (left == smallest)
			 {
				 degree = Constants.LEFT_ROTATE;
			 }
			else if (right == smallest)
			 {
				 degree = Constants.RIGHT_ROTATE;
			 }
			else if (smallest == behind)
			 {
				 degree = 180;
			 }
			 
		return degree;
	}
	
	@Override
	public void move(int distance, boolean forward) throws HitObstacleException {
		int moveCount = 0;
		int degree = 0;
		while (moveCount < distance)
		{
			degree = findDirectionClosestToExit();
			try
			{
				switch(degree)
				{
				case 0:
				{
					break;
				}
				case Constants.LEFT_ROTATE:
				case Constants.RIGHT_ROTATE:
				{
					rotate(degree);
					break;
				}
				case 180:
				{
					rotate(Constants.LEFT_ROTATE);
					rotate(Constants.LEFT_ROTATE);
					break;
				}
					
				}
			}
			catch (UnsupportedArgumentException e) 
			{
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			}
			moveCount++;
			maze.keyDown(null, Event.UP);
		}
	}
	
	@Override
	public boolean isAtGoal()
	{
		try
		{
			//always false
			return (maze.mazedists.getDistance(getCurrentPosition()[0] , getCurrentPosition()[1]) == 0);
			//return false;
		}
		catch(ArrayIndexOutOfBoundsException e)
		{
			return true;
		}
	}

}
